<?php

class Ext_Model_WSProxy
{

    protected $_model = null;
    protected $_isADbTable = false;

    private $_localClass = null;

    private $_remoteConstructorArgs = null;
    private $_remoteData = null;
    private $_remoteClassId = null;
    private $_remoteInSync = false;
    private $_client = null;

    function __construct() {
        $this->setModel();
    }

    function  __call($name, $args) {
        if ( empty( $this->_model ) ) {
            throw new Exception( 'Model class name must be declared');
        }

        if ( $this->_localClass != null ) {
            if ( ! method_exists( $this->_localClass, $name ) ) {
                throw new Exception("Method $name don't exists in class " . $this->_model );
            }
            return call_user_func_array(array( $this->_localClass, $name ), $args );
        }

        $arguments = $this->processArguments( $args );
        if ( ! $this->_remoteInSync ) {
            $ret = call_user_func( array( $this->_client, 'call' )
                , $this->_remoteClassId
                , $this->_model
                , $this->_remoteConstructorArgs
                , $this->_remoteData
                , $name, $arguments );
        } else {
            $ret = call_user_func( array( $this->_client, 'callInSync' )
                , $this->_remoteClassId
                , $name, $arguments );
        }
        $this->_remoteInSync = $ret[ 'remoteInSync' ];
        return $this->processRetval( $ret );
    }

    // OBS: No puedo reconstruir el estado estatico del objeto remoto por que no existe aun un
    // staticGet y staticSet, asi que esta llamada debe ser usada con cuidado !!!
    function   __callStatic($name, $args) {
        if ( empty( $this->_model ) ) {
            throw new Exception( 'Model class name must be declared');
        }

        if ( $this->_localClass != null ) {
            if ( ! method_exists( $this->_model, $name ) ) {
                throw new Exception("Static Method $name don't exists in class " . $this->_model );
            }
            return call_user_func_array(array( $this->_model, $name ), $args );
        }

        $arguments = $this->processArguments( $args );
        $ret = call_user_func( array( $this->_client, 'callStatic' )
            , $this->_model
            , $name, $arguments );
        return $this->processRetval( $ret );
    }

    private function processArguments( $arguments ) {
        $retval = array();
        foreach( $arguments as $argument ) {
            $retval[] = base64_encode( serialize( $argument ) );
        }
        return $retval;
    }

    private function processRetval( $ret ) {
        $retval = unserialize( base64_decode( $ret[ 'retval' ] ) );
        if ( ! isset( $ret['class'] ) ) {
            return $retval;
        } else {
            if ( !class_exists( $ret[ 'class' ] ) ) {
                throw new Exception("Cant dezerialize class " . $ret['class'] );
            }
        }
        // TODO: Probar y mejorar esto !!!
        if ( $this->_isADbTable) {
            if ( is_a( $retval , 'Ext_Db_Table_Row' ) ) {
                $retval->setTableInternal( $this );
            }
            if ( is_a( $retval , 'Ext_Db_Table_Rowset' ) ) {
                foreach( $retval as $row ) {
                    $row->setTableInternal( $this );
                }
            }
        }
        return $retval;
    }

    function  __get($name) {
        if ( $this->_localClass != null ) {
            if ( ! property_exists( $this->_localClass, $name ) ) {
                throw new Exception("Property $name don't exists in class " . $this->_model );
            }
            return $this->_localClass->$name;
        }
        if ( isset( $this->_remoteData[ $name ] ) ) {
            return $this->_remoteData[ $name ];
        }
        throw new Exception( "Undefined parameter $name in class " . $this->_model );
    }

    function __set($name, $value) {
        if ( empty( $this->_model ) ) {
            throw new Exception( 'Model class name must be declared');
        }
        if ( $this->_localClass != null ) {
            return $this->_localClass->$name = $value;
        }
        $this->_remoteData[ $name ] = $value;
        $this->_remoteInSync = false;
    }

    function  __isset($name) {
        if ( empty( $this->_model ) ) {
            throw new Exception( 'Model class name must be declared');
        }
        if ( $this->_localClass != null ) {
            return isset( $this->_localClass->$name );
        }
        return isset( $this->_remoteData[ $name ] );
    }

    function __unset($name) {
        if ( empty( $this->_model ) ) {
            throw new Exception( 'Model class name must be declared');
        }
        if ( $this->_localClass != null ) {
            unset( $this->_localClass->$name );
            return;
        }
        unset( $this->_remoteData[ $name ] );
        $this->_remoteInSync = false;
    }

    public function isADbTable() {
        return $this->_isADbTable;
    }

    public function doItADbTable($_isADbTable) {
        $this->_isADbTable = $_isADbTable;
    }


    public function getModel() {
        return $this->_model;
    }

    public function setModel( $model = null ) {

        if ( $model != null ) {
            $this->_model = $model;
        }
        $args = func_get_args();
        if ( class_exists( $this->_model ) ) {
            $reflectionObj = new ReflectionClass( $this->_model );
            $this->_localClass = $reflectionObj->newInstanceArgs( $args );
        } else {
            $this->_remoteConstructorArgs = $args;
            $this->_remoteClassId = uniqid();

            $config = Zend_Registry::get( 'config' );
            if (! isset( $config->webServices ) || ! isset( $config->webServices->{$this->_model} )) {
                throw new Exception("Remote webservice " . $this->_model . " must be configured in config");
            }
            // @todo: Usar Zend_Rest_Client dado que finalmente serializo todo !!!
            $this->_client = new Zend_Soap_Client( $config->webServices->{$this->_model} );
        }
    }

}